import cv2;
import mediapipe as mp;
import math;
from pynput.keyboard import Key,Controller;
import time;
import threading;
from multiprocessing import Process;
import asyncio;

# 获取相机
capture = cv2.VideoCapture(0);

capWidth =int(capture.get(4));
capHeight = int(capture.get(3));

mpHands = mp.solutions.hands; # 手部侦测模型

hands = mpHands.Hands(min_detection_confidence=0.5,min_tracking_confidence=0.9); # 手部侦测基类
mpDraw = mp.solutions.drawing_utils; # 绘画

# 设置点的样式
handLmSpec = mpDraw.DrawingSpec(color=(0,0,255),thickness=5);
# 设置线的样式
handConSpec = mpDraw.DrawingSpec(color=(0,255,0),thickness=10);

# FPS
pTime = 0;
cTime = 0;

keyboard = Controller();

# 绘制所有键
rectWidth = 45;
rectHeight = 45;
def drawAll(img,buttonList):
    rect = {};
    rectList = [];
    sPosX = 30;
    sPosY = 50;
    for buttons in buttonList:
        sPosY += rectHeight + 25;
        i = 0;
        #if (buttonList.index(buttons) == 3):
         #   rectWidth = 160

        for button in buttons:
            # 绘制方框
            cv2.rectangle(img,(sPosX + i * rectWidth + 30, sPosY)
                          ,(((sPosX + i * rectWidth) + rectWidth+25),sPosY + rectHeight),
                          (13,255,3),1);
            # 绘制字
            cv2.putText(img,button,(sPosX + i * rectWidth+40,sPosY+35)
                        ,0,1,(255,255,255),2,1);
            i += 1;
            rect.update({str(button):[(sPosX + i * rectWidth + 30,sPosY),
                        (((sPosX + i * rectWidth) + rectWidth+25),sPosY + rectHeight)]});
            rectList.append(rect);
    return  rectList;

# 根据字典的值去查找键

def getKeyByValue(dict_data, value):
    for key, val in dict_data.items():
        if val == value:
            return key;

# 计算两点间距离的公式
def getDistance(p1,p2):
    deltaX = p1.x - p2.x;
    deltaY = p1.y - p2.y;
    distance = math.sqrt(deltaX ** 2 + deltaY ** 2);
    return distance;

# 延时调用
var = 0;
def delayed_execution(delay_seconds, task):
    #global var;
    #while True:
     #   var += 1;
    """"
    延时执行任务的函数
    :param delay_seconds: 延时的秒数
    :param task: 需要执行的任务函数
    """
    time.sleep(delay_seconds);
    task();

# 键入指定键
def pressKey(key):
    if key == 'space':
        keyboard.press(Key.space);
    if key == '!':
        keyboard.press(Key.shift_l);
    if key == "ctrl":
        keyboard.press(Key.ctrl_l);
    if key == "enter":
        keyboard.press(Key.enter);
    if key == "toggle":
        keyboard.press(Key.ctrl_l);
        keyboard.press(Key.shift_l);
    if key == '-':
        keyboard.press(Key.backspace)
    if key != 'enter' and key != 'space'\
        and key != '!' and key != 'ctrl' and key != 'toggle' \
            and key != '-':
            keyboard.press(key);
    print(key);

# keys
keys = [['Q','W','E','R','T','Y','U','I','O','P','-'],
        ['A','S','D','F','G','H','J','K','L',';','Enter'],
        ['!','Z','X','C','V','B','N','M',',','.','?'],
        ['Ctrl','Space','Toggle']];

keyList = [];

flag = True;

def toggleFlag():
    global flag
    flag = True;

while True:
    # print(flag);
    # print(len(threading.enumerate()));
    ret , frame = capture.read();
    ret, imgFrame = capture.read();
    if ret:
        # 绘制虚拟键盘
        rects = drawAll(img=frame, buttonList=keys);
        # 将frame由bgr -> rgb
        img = cv2.cvtColor(imgFrame,cv2.COLOR_BGR2RGB);
        # 进行处理
        result = hands.process(img);
        # 获取视图窗口的宽度和高度
        frameHeight = frame.shape[0];
        frameWidth = frame.shape[1];
        # 标记每一个关键点(共21个)
        if result.multi_hand_landmarks:
            for handLandmarks in result.multi_hand_landmarks:
                # 标记点的位置
                mpDraw.draw_landmarks(imgFrame,handLandmarks,mpHands.HAND_CONNECTIONS,handLmSpec,handConSpec);
                # 食指指尖
                fingerTipLandmark = handLandmarks.landmark[mp.solutions.hands.HandLandmark.INDEX_FINGER_TIP];
                touchPosX = int(fingerTipLandmark.x * frameWidth);
                touchPosY = int(fingerTipLandmark.y * frameHeight);
                # 中指指尖
                middleTipLandmark = handLandmarks.landmark[mp.solutions.hands.HandLandmark.MIDDLE_FINGER_TIP];
                subTouchPosX = int(middleTipLandmark.x * frameWidth);
                subTouchPosY = int(middleTipLandmark.y * frameHeight);
                # 计算食指指尖和中指指尖间的距离
                distance = getDistance(fingerTipLandmark,middleTipLandmark);
                # 获取每一个关键点的坐标
                for i , landmark in enumerate(handLandmarks.landmark):
                    xPos = int(landmark.x * frameWidth);
                    yPos = int(landmark.y * frameHeight);
                    # 标记点的名称
                    cv2.putText(imgFrame,str(i),(xPos-25,yPos+5),cv2.FONT_HERSHEY_SIMPLEX,0.4,(0,0,255),2);

                    # 判断点击
                    handPos = (touchPosX, touchPosY);

                    # print(distance);
                    if flag:
                        for rect in rects:
                            for value in rect.values():
                                if value[0][0] <= handPos[0]+rectWidth <= value[1][0] and \
                                        value[0][1] <= handPos[1] <= value[1][1]:
                                    # 高亮显示
                                    cv2.rectangle(frame, (value[0][0] - rectWidth, value[0][1])
                                                  , (value[1][0] - rectWidth, value[1][1]),
                                                  (255, 3, 255), 1);
                                    if distance <= 0.06:
                                        # 键入按下的虚拟键
                                        keyBoardValue = getKeyByValue(rect, value).lower();
                                        keyList.append(keyBoardValue);
                                        if (len(keyList) >= 2):
                                            if (keyList[-1] == keyList[-2]):
                                                flag = False;
                                                # 创建一个线程，延时1.5秒执行任务
                                                thread = threading.Thread(target=delayed_execution,
                                                                          args=(1.5, toggleFlag));
                                                thread.daemon = True;
                                                thread.start();
                                                # loop = asyncio.get_event_loop();
                                                # loop.run_until_complete(delayed_execution(1.5,toggleFlag))
                                                # asyncio.run(delayed_execution(1.5,toggleFlag))
                                            if flag:
                                                # 高亮显示
                                                cv2.rectangle(frame, (value[0][0] - rectWidth, value[0][1])
                                                              , (value[1][0] - rectWidth, value[1][1]),
                                                              (3, 13, 255), 1);
                                                pressKey(keyBoardValue);
        # FPS的显示
        cTime = time.time();
        fps = 1 / (cTime - pTime);
        pTime = cTime;
        cv2.putText(frame, f"FPS {int(fps)}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2);

        # 显示每一帧
        cv2.imshow("Frame",frame);
        cv2.imshow("imgFrame",imgFrame);
    # q键退出
    if cv2.waitKey(1) == ord('q'):
        break;